/*
HttpSharp: Modular HTTP server C# library targeting .NET/Mono
Copyright 2009 Thesis Kongpakdeepong

This file is part of HttpSharp.

HttpSharp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

HttpSharp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with HttpSharp.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;

namespace HttpSharp
{
  public partial class HttpRequest
  {
    private byte[] _rawRequest;
    private int _rawRequestOffset;
    private int _headerLength;
    private Socket _connection;
    
    private string _header;
    public string Header
    {
      get { return _header; }
      private set { _header = value; }
    }
    
    private Dictionary<RequestHeader ,string> _headerValues;
    public string this[RequestHeader key]
    {
      get
      {
        if(_headerValues.ContainsKey(key))
        {
          return _headerValues[key];
        }
        else
        {
          return string.Empty;
        }
      }
      private set { _headerValues[key] = value; }
    }
    
    public Dictionary<RequestHeader ,string>.KeyCollection Keys
    {
      get { return _headerValues.Keys; }
    }
    
    public HttpRequest(Socket connection):this()
    {
      _connection = connection;
      _rawRequest = new byte[1024];
      _rawRequestOffset = 0;
      _headerLength = 0;
      _headerValues = new Dictionary<RequestHeader, string>();
      SkipLeadingCrLf();
      ReceiveHeader();
      ParseHeader();
    }
    private void SkipLeadingCrLf()
    {
      while(_rawRequestOffset < 1)
      {
        _rawRequestOffset += _connection.Receive(_rawRequest, 0, 2, SocketFlags.None);
        if(Encoding.ASCII.GetString(_rawRequest, 0, _rawRequestOffset) == CrLf)
        {
          _rawRequestOffset = 0;
        }
      }
    }
    
    private void ReceiveHeader()
    {
      while(_headerLength < 1)
      {
        _rawRequestOffset += _connection.Receive(_rawRequest, _rawRequestOffset, _rawRequest.Length - _rawRequestOffset, SocketFlags.None);
        int endOfHeaderIndex = Encoding.ASCII.GetString(_rawRequest, 0, _rawRequestOffset).IndexOf(Terminator, StringComparison.Ordinal);
        if(endOfHeaderIndex > 0)
        {
          _headerLength = endOfHeaderIndex + Terminator.Length;
        }
        if(_rawRequestOffset == _rawRequest.Length)
        {
          Array.Resize(ref _rawRequest, _rawRequest.Length*2);
        }
      }
      Header = Encoding.ASCII.GetString(_rawRequest, 0, _headerLength);
    }
    
    private void ParseHeader()
    {
      string[] headers = Header.Split(new string[] {CrLf}, StringSplitOptions.RemoveEmptyEntries);
      ParseRequestLine(headers[0]);
      RequestHeader lastHeader = new RequestHeader();
      for(int i = 1; i < headers.Length; i++)
      {
        if(headers[i].StartsWith(" ") || headers[i].StartsWith("\t"))
        {
          this[lastHeader] += headers[i].Trim();
          continue;
        }
        string[] fieldTokens = headers[i].Split(new char[]{':'}, 2);
        if(HeaderMap.ContainsKey(fieldTokens[0]))
        {
          this[HeaderMap[fieldTokens[0]]] = fieldTokens[1].Trim();
          lastHeader = HeaderMap[fieldTokens[0]];
        }
      }
    }
    
    private void ParseRequestLine(string requestLine)
    {
      string[] requestLineTokens = requestLine.Split(' ');
      this[RequestHeader.Method] = requestLineTokens[0];
      this[RequestHeader.Uri] = requestLineTokens[1];
      this[RequestHeader.Version] = requestLineTokens[2];
    }
  }
}